// --------------------------------------------------------------------------------------------------------------------
// <copyright file="UIManualOrder.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework.Forge.UI.Forms
{
    #region Using Directives

    using System;
    using System.Windows.Forms;

    using Org.OpenTrader.Framework.Forge.Enums;
    using Org.OpenTrader.Framework.Forge.Events;
    using Org.OpenTrader.Framework.Forge.Interfaces;
    using Org.OpenTrader.Framework.UI.Forms;

    #endregion

    /// <summary>
    /// The ui manual order.
    /// </summary>
    public partial class UIManualOrder : UIControl
    {
        #region Constants and Fields

        /// <summary>
        /// The instrument.
        /// </summary>
        private readonly IInstrument instrument;

        /// <summary>
        /// The order.
        /// </summary>
        private readonly IOrder order;

        /// <summary>
        /// The tracker.
        /// </summary>
        private readonly IOrderTracker tracker;

        /// <summary>
        /// The mode.
        /// </summary>
        private EMode mode;

        /// <summary>
        /// The modifyrejectioncount.
        /// </summary>
        private int modifyrejectioncount;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="UIManualOrder"/> class.
        /// </summary>
        public UIManualOrder()
        {
            this.InitializeComponent();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="UIManualOrder"/> class.
        /// Use this constructor for order modify
        /// </summary>
        /// <param name="order">
        /// </param>
        /// <param name="tracker">
        /// The tracker.
        /// </param>
        public UIManualOrder(IOrder order, IOrderTracker tracker)
        {
            this.mode = EMode.Modify;
            this.InitializeComponent();
            this.order = order;
            this.tracker = tracker;
            this.instrument = tracker.Instrument;
            this.uiMarketBook1.Instrument = this.instrument;
            this.instrument.OrderTrackingEventCB += this.OnInstrumentOrderTrackingEventCB;
            this.Init();

            this.buttonSend.Text = "Modify";
            this.updownPrice.Value = (decimal)this.order.Price;
            this.updownQuantity.Value = this.order.Quantity;
            this.comboType.SelectedItem = this.order.Type;
            this.comboType.Enabled = false;

            this.DoUpdate();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="UIManualOrder"/> class.
        /// Use this constructor to create a new order
        /// </summary>
        /// <param name="instrument">
        /// The instrument.
        /// </param>
        /// <param name="manualOrderMode">
        /// The type.
        /// </param>
        public UIManualOrder(IInstrument instrument, EManualOrderMode manualOrderMode)
        {
            this.mode = EMode.Create;
            this.InitializeComponent();
            this.ManualOrderMode = manualOrderMode;
            this.instrument = instrument;
            this.uiMarketBook1.Instrument = instrument;

            this.Init();

            try
            {
                if (this.ManualOrderMode == EManualOrderMode.NewOrderBuy)
                {
                    this.buttonSend.Text = "Buy";
                    this.updownPrice.Value = (decimal)this.instrument.MarketBook.Asks[0].Price;
                }
                else if (this.ManualOrderMode == EManualOrderMode.NewOrderSell)
                {
                    this.buttonSend.Text = "Sell";
                    this.updownPrice.Value = (decimal)this.instrument.MarketBook.Bids[0].Price;
                }
            }
            catch (Exception)
            {
            }

            this.DoUpdate();
        }

        #endregion

        #region Delegates

        /// <summary>
        /// The cross thread action.
        /// </summary>
        private delegate void CrossThreadAction();

        #endregion

        #region Enums

        /// <summary>
        /// The e type.
        /// </summary>
        public enum EManualOrderMode
        {
            /// <summary>
            /// The new order sell.
            /// </summary>
            NewOrderSell, 

            /// <summary>
            /// The new order buy.
            /// </summary>
            NewOrderBuy, 
        }

        /// <summary>
        /// The e mode.
        /// </summary>
        private enum EMode
        {
            /// <summary>
            /// The create.
            /// </summary>
            Create, 

            /// <summary>
            /// The modify.
            /// </summary>
            Modify
        }

        #endregion

        #region Properties

        /// <summary>
        /// The Way.
        /// </summary>
        public EManualOrderMode ManualOrderMode { get; private set; }

        /// <summary>
        /// Gets OrderType.
        /// </summary>
        private EOrderType OrderType
        {
            get
            {
                return (EOrderType)this.comboType.SelectedItem;
            }
        }

        /// <summary>
        /// Gets Price.
        /// </summary>
        private double Price
        {
            get
            {
                return (double)this.updownPrice.Value;
            }
        }

        /// <summary>
        /// Gets Quantity.
        /// </summary>
        private int Quantity
        {
            get
            {
                return (int)this.updownQuantity.Value;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// The on enter.
        /// </summary>
        /// <param name="e">
        /// The e.
        /// </param>
        protected override void OnEnter(EventArgs e)
        {
            base.OnEnter(e);

            this.DoUpdate();
        }

        /// <summary>
        /// The on handle destroyed.
        /// </summary>
        /// <param name="e">
        /// The e.
        /// </param>
        protected override void OnHandleDestroyed(EventArgs e)
        {
            if (this.mode == EMode.Modify)
            {
                this.instrument.OrderTrackingEventCB -= this.OnInstrumentOrderTrackingEventCB;
            }

            base.OnHandleDestroyed(e);
        }

        /// <summary>
        /// The on leave.
        /// </summary>
        /// <param name="e">
        /// The e.
        /// </param>
        protected override void OnLeave(EventArgs e)
        {
            base.OnLeave(e);
            this.DoUpdate();
        }

        /// <summary>
        /// The update.
        /// </summary>
        private void DoUpdate()
        {
            // Maximum size is maxtrade/currenPrice
            this.updownQuantity.Maximum = ((double)this.updownPrice.Value < Constants.Zero)
                                              ? 0
                                              : (decimal)(this.instrument.InstrumentStaticData.MaxNominalTrade / (double)this.updownPrice.Value);

            // Price
            this.updownPrice.Increment = (decimal)this.instrument.InstrumentStaticData.TickSizeRule.Tick(ExpoRefPriceRule.Standard(this.instrument));
            this.updownPrice.DecimalPlaces = (int)(-Math.Log10((double)this.updownPrice.Increment));
        }

        /// <summary>
        /// The handler_ send clicked.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void HandlerSendClicked(object sender, EventArgs e)
        {
            if (this.mode == EMode.Create)
            {
                var orders = new Guid[] { };

                switch (this.ManualOrderMode)
                {
                    case EManualOrderMode.NewOrderSell:
                        orders = this.instrument.Sell(this.OrderType, this.Price, this.Quantity, "/Manual");
                        break;

                    case EManualOrderMode.NewOrderBuy:
                        orders = this.instrument.Buy(this.OrderType, this.Price, this.Quantity, "/Manual");
                        break;
                }

                if (orders.Length == 0)
                {
                    // Failed
                    MessageBox.Show("Order was not sent, see security service", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            else if (this.mode == EMode.Modify)
            {
                var neworder = new Order(
                    this.order.OrderProviderID, this.order.InstrumentID, this.order.Type, this.order.Way, this.Price, this.Quantity, this.order.BookingPath);
                var modifystatus = this.tracker.ModifyOrder(this.order, neworder);
                if (!modifystatus)
                {
                    // Failed
                    MessageBox.Show(modifystatus.DetailedMessage, "Modify failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    this.buttonSend.Enabled = true;
                }
                else
                {
                    // Success : disable button until some async thing occurs
                    this.buttonSend.Enabled = false;
                }
            }
        }

        /// <summary>
        /// The init.
        /// </summary>
        private void Init()
        {
            // Set types
            this.comboType.Items.Add(EOrderType.Limit);
            this.comboType.Items.Add(EOrderType.FillOrKill);
            this.comboType.SelectedIndex = 0;

            // Size
            this.updownQuantity.DecimalPlaces = 0;
            this.updownQuantity.Value = 0;
            this.updownQuantity.Minimum = 0;
            var lotsize = this.instrument.InstrumentStaticData.LotSize;
            this.updownQuantity.Increment = (decimal)(lotsize > 0.0 ? (double)lotsize : 1.0);

            // Price
            this.updownPrice.Minimum = 0;
        }

        /// <summary>
        /// The instrument_ order tracking event cb.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnInstrumentOrderTrackingEventCB(object sender, OrderTrackingEventArgs e)
        {
            // Something related to this particular order has happened
            if (e.OrderTrackingCell.Order.ID == this.order.ID)
            {
                if (e is OrderTrackingEventArgs.AsynchronousUpdate)
                {
                    var args = e as OrderTrackingEventArgs.AsynchronousUpdate;

                    // The modification rejection counter increased, that is, our modify failed -or- modified
                    if (args.OrderTrackingCell.Modified || args.OrderTrackingCell.ModifyRejected > this.modifyrejectioncount)
                    {
                        // This event is not distributed by the UI Threads. Make it X-thread safe
                        CrossThreadAction d = delegate
                            {
                                // Reactivate the "modify" button in order to allow the user to try again
                                this.buttonSend.Enabled = true;
                            };

                        // X-thread safe call
                        this.ContainingForm.BeginInvoke(d);
                    }

                    if (args.OrderTrackingCell.Closed)
                    {
                        // This event is not distributed by the UI Threads. Make it X-thread safe
                        CrossThreadAction d = delegate
                            {
                                // The order is not alive anymore (fully executed, cancelled, etc ...). HardOrSoftClosed the modify form.
                                this.ContainingForm.Destroy();
                            };

                        // X-thread safe call
                        this.ContainingForm.BeginInvoke(d);
                    }

                    // Keep track of the count of modify failures
                    this.modifyrejectioncount = args.OrderTrackingCell.ModifyRejected;
                }
            }
        }

        /// <summary>
        /// The updownPrice_ click.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnUpdownPriceClick(object sender, EventArgs e)
        {
            this.DoUpdate();
        }

        /// <summary>
        /// The updownPrice_ value changed.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnUpdownPriceValueChanged(object sender, EventArgs e)
        {
            this.DoUpdate();
        }

        #endregion
    }
}